#include "NetControllerUDP.h"


CNetControllerUDP::CNetControllerUDP(void)
{
	nMaxClientId = 0;
	nMaxServerId = 0;
}

CNetControllerUDP::~CNetControllerUDP(void)
{
}

// ----------- client specific -----------
int CNetControllerUDP::addClient(string strIp, unsigned short usPort)
{
	SClientUDP* client = new SClientUDP();
	client->id = ++nMaxClientId;
	client->ip = strIp;
	client->port = usPort;
	client->udp = new UDPManager();

	int size = (int) strIp.size();
	char *acIp = new char[size + 1];
	memcpy(acIp, strIp.c_str(), strIp.size());
	acIp[strIp.size()] = '\0';

	if (client->udp->Create())
	{
		if (client->udp->Connect(acIp, usPort))
		{
			// ready
			client->udp->SetTimeoutSend(0);
			client->udp->SetTimeoutReceive(0);

			// add client to hash map
			mapClients[client->id] = client;

			delete [] acIp;
			return client->id;
		}
	}

	// error occured -> clean up
	client->udp->Close();
	delete client->udp;
	delete client;
	delete [] acIp;
	--nMaxClientId;

	return 0;
}

void CNetControllerUDP::removeClient(int id)
{
	ClientMapUDP::iterator it = mapClients.find(id);

	// check if this is a known id
	if (it != mapClients.end())
	{
		delete ((SClientUDP*) it->second)->udp;
		delete ((SClientUDP*) it->second);
		mapClients.erase(it);
	}
}

bool CNetControllerUDP::send(unsigned short usType, string strData)
{
	string strMessage = createMessage(usType, strData);

	// iterate through all clients and send message
	ClientMapUDP::iterator it;
	bool bSuccess = true;
	for (it = mapClients.begin(); it != mapClients.end(); it++)
	{
		SClientUDP* client = (SClientUDP*) it->second;
		strMessage += strData;
		int nReturn = client->udp->Send(strMessage.data(), (int) strMessage.size());
		if (nReturn == SOCKET_ERROR)
		{
			//LOG_ERROR((T("socket error sending to client %hs:%u"), (const char*)client->ip.c_str(), client->port));
			bSuccess = false;
		}
	}
	return bSuccess;
}

bool CNetControllerUDP::send(unsigned short usType, string strData, int nId)
{
	// compose send message
	string strMessage = createMessage(usType, strData);

	// find the client and send message
	ClientMapUDP::iterator it = mapClients.find(nId);
	bool bSuccess = true;
	if (it != mapClients.end())
	{
		SClientUDP* client = (SClientUDP*) it->second;
		strMessage += strData;
		int nReturn = client->udp->Send(strMessage.c_str(), (int) strMessage.size());
		if (nReturn == SOCKET_ERROR)
		{
			//LOG_ERROR((T("socket error sending to client %hs:%u"), (const char*)client->ip.c_str(), client->port));
			bSuccess = false;
		}
	}
	else
	{
		//LOG_ERROR((T("unknown client id %d"), nId));
		bSuccess = false;
	}

	return bSuccess;
}

string CNetControllerUDP::createMessage(unsigned short usType, string strData)
{
	// convert unsigned short to "byte arrays"
	unsigned int unDataSize = unsigned(strData.size());
	string strDataSize = string((char *) &unDataSize, 4);

	string strType = string((char *) &usType, 2);

	return "!" + strDataSize + strType + "!?";
}

// ----------- server specific -----------

int CNetControllerUDP::addServer(unsigned short usPort)
{
	SServerUDP* server = new SServerUDP();
	server->id = ++nMaxServerId;
	server->port = usPort;
	server->udp = new UDPManager();

	if (server->udp->Create())
	{
		if (server->udp->Bind(usPort))
		{
			// ready
			server->udp->SetTimeoutSend(0);
			server->udp->SetTimeoutReceive(0);

			// add server to hash map
			mapServers[server->id] = server;

			return server->id;
		}
	}

	// error occured -> clean up
	server->udp->Close();
	delete server->udp;
	delete server;
	--nMaxServerId;

	return 0;
}

void CNetControllerUDP::removeServer(int id)
{
	ServerMapUDP::iterator it = mapServers.find(id);

	// check if this is a known id
	if (it != mapServers.end())
	{
		delete ((SServerUDP*) it->second)->udp;
		delete ((SServerUDP*) it->second);
		mapServers.erase(it);
	}
}

bool CNetControllerUDP::receive(unsigned short* usType, string* strData, int nId)
{
	static char acBuffer[BUFFER_SIZE] = "";

	// find the server and listen
	ServerMapUDP::iterator it = mapServers.find(nId);
	bool bSuccess = true;

	*usType = MSG_NONE;

	if (it != mapServers.end())
	{
		SServerUDP* server = (SServerUDP*) it->second;

		int nReturn = server->udp->Receive(acBuffer, 256);
		if (nReturn == SOCKET_ERROR)
		{
			bSuccess = false;
		}
		else if (nReturn == SOCKET_TIMEOUT)
		{
			bSuccess = false;
		}
		else if (acBuffer[0] == '!' && acBuffer[7] == '!' && acBuffer[8] == '?')
		{
			// extract message header by casting 4 chars back to unsigned int
			unsigned int unDataSize = *(unsigned int*) &acBuffer[1];
			*usType = *(unsigned short*) &acBuffer[5];

			// check if the data size fits the returned buffer
			if (unDataSize == (unsigned)(nReturn - 9))
			{
				char* acData = new char[unDataSize];
				// fetch data
				memcpy(acData, acBuffer + 9, unDataSize);
				*strData = string(acData, unDataSize);
				// clean up
				delete [] acData;
			}
			else
			{
				//LOG_ERROR((T("receive error (data) for server on port %u"), server->port));
				bSuccess = false;
			}
		}
	}
	else
	{
		//LOG_ERROR((T("unknown server id %d"), nId));
		bSuccess = false;
	}

	return bSuccess;
}
